/**
 * Copyright (c) 2008, Andrew Carter
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are 
 * permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer. Redistributions in binary form must reproduce 
 * the above copyright notice, this list of conditions and the following disclaimer in 
 * the documentation and/or other materials provided with the distribution. Neither the 
 * name of Andrew Carter nor the names of contributors may be used to endorse or promote 
 * products derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.acarter.scenemonitor.dialog;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;

// noel lynch 21-08-08 : added mouse info import for java 5 compatability
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.net.URL;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;

import com.acarter.scenemonitor.util.Constants;
import com.acarter.scenemonitor.util.VerticalLabelUI;

/**
 * <p>
 * A_ToolDialog
 * </p>
 * <p>
 * Abstract implementation providing features such that the content pane of the
 * dialog may be hidden/shown.
 * </p>
 * <p>
 * Copyright (c) 2008
 * </p>
 * 
 * @author Andrew Carter
 */
public abstract class A_ToolDialog extends JDialog {

    /**  */
    private static final long serialVersionUID = 1L;

    /** The bar of the dialog used for dragging, contains the button panel. */
    private JPanel basePanel;

    /** Contains the close and show/hide buttons */
    private JPanel buttonPanel;

    /** The pseudo content pane */
    private Container contentPane;

    /** The content pane frame */
    private JPanel contentFrame;

    /** Flag indicates if the content pane is hidden */
    private boolean hidden;

    /** Flag indicates which side of the base panel the content pane is on */
    private boolean leftSide;

    /** Icon for the hide button */
    private Icon hideIcon;

    /** Icon for the show button */
    private Icon showIcon;

    /** Icon for the close button */
    private Icon closeIcon;

    /** Icon for the help button */
    private Icon helpIcon;

    /** Icon for the resize button */
    private Icon resizeIcon;

    /** Icon for the hide roll over button */
    private Icon hideOverIcon;

    /** Icon for the show roll over button */
    private Icon showOverIcon;

    /** Icon for the close roll over button */
    private Icon closeOverIcon;

    /** Icon for the help roll over button */
    private Icon helpOverIcon;

    /** Icon for the resize roll over button */
    private Icon resizeOverIcon;

    /** The actual show/hide button */
    private JLabel stateButton;

    /**
     * Tracks the dialog translation used during show/hide operations so the
     * dialog doesn't jump around
     */
    private int tempTranslation;

    /** Flag use to indicate the dialog is being dragged */
    private boolean moveStart = false;

    /** Flag used to indicate the dialog is being resizes */
    private boolean resizeStart = false;

    /** Reference point for dialog dragging */
    private Point mouseClickPoint = null;

    /** Dimensions of the display, used to determine when to switch sides */
    private Dimension screenSize;

    /**
     * Constructor.
     * 
     * @param owner
     *            Frame which is the parent of this dialog, or null
     * @param title
     *            Title of the dialog
     * @param modal
     *            True if model, false otherwise
     */
    public A_ToolDialog(Frame owner, String title, boolean modal) {

	super(owner, title, modal);

	hidden = false;
	leftSide = true;
	screenSize = getToolkit().getScreenSize();

	setUndecorated(true);

	build();
    }

    /**
     * Construct the dialog and components.
     */
    private void build() {

	initIcons();

	final JLabel close = new JLabel(closeIcon);
	close.setToolTipText("Close");

	close.addMouseListener(new MouseListener() {

	    public void mouseClicked(MouseEvent e) {

		close();
	    }

	    public void mouseEntered(MouseEvent e) {

		close.setIcon(closeOverIcon);
	    }

	    public void mouseExited(MouseEvent e) {

		close.setIcon(closeIcon);
	    }

	    public void mousePressed(MouseEvent e) {
	    }

	    public void mouseReleased(MouseEvent e) {
	    }
	});

	if(hidden)
	    stateButton = new JLabel(showIcon);
	else
	    stateButton = new JLabel(hideIcon);

	stateButton.setToolTipText("Show/Hide");
	stateButton.addMouseListener(new MouseListener() {

	    public void mouseClicked(MouseEvent e) {

		onShowHide();
	    }

	    public void mouseEntered(MouseEvent e) {

		if(hidden)
		    stateButton.setIcon(showOverIcon);
		else
		    stateButton.setIcon(hideOverIcon);
	    }

	    public void mouseExited(MouseEvent e) {

		if(hidden)
		    stateButton.setIcon(showIcon);
		else
		    stateButton.setIcon(hideIcon);
	    }

	    public void mousePressed(MouseEvent e) {
	    }

	    public void mouseReleased(MouseEvent e) {
	    }
	});

	final JLabel help = new JLabel(helpIcon);
	help.setToolTipText("Help");

	help.addMouseListener(new MouseListener() {

	    public void mouseClicked(MouseEvent e) {

		onHelp();
	    }

	    public void mouseEntered(MouseEvent e) {

		help.setIcon(helpOverIcon);
	    }

	    public void mouseExited(MouseEvent e) {

		help.setIcon(helpIcon);
	    }

	    public void mousePressed(MouseEvent e) {
	    }

	    public void mouseReleased(MouseEvent e) {
	    }
	});

	JLabel labelTitle = new JLabel(getTitle());
	labelTitle.setUI(new VerticalLabelUI(false));
	labelTitle.setFont(new Font("Helvetica", Font.BOLD, 11));

	buttonPanel = new JPanel();
	buttonPanel.setBorder(BorderFactory
		.createCompoundBorder(BorderFactory.createRaisedBevelBorder(), BorderFactory.createEmptyBorder(4, 4, 4, 4)));
	buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS));

	buttonPanel.add(Box.createRigidArea(new Dimension(0, 5)));
	buttonPanel.add(close);
	buttonPanel.add(Box.createRigidArea(new Dimension(0, 10)));
	buttonPanel.add(stateButton);
	buttonPanel.add(Box.createRigidArea(new Dimension(0, 10)));
	buttonPanel.add(labelTitle);
	buttonPanel.add(Box.createGlue());
	buttonPanel.add(Box.createRigidArea(new Dimension(0, 15)));
	buttonPanel.add(help);
	buttonPanel.add(Box.createRigidArea(new Dimension(0, 5)));
	buttonPanel.addMouseMotionListener(new MouseMotionListener() {

	    public void mouseDragged(MouseEvent mouseEvent) {

		if(moveStart)
		    moveWindowTo(mouseEvent.getPoint());
	    }

	    public void mouseMoved(MouseEvent mouseEvent) {
	    }

	});

	buttonPanel.addMouseListener(new MouseAdapter() {

	    @Override
	    public void mousePressed(MouseEvent mouseEvent) {

		mouseClickPoint = mouseEvent.getPoint();
		moveStart = true;

		onBeginDragWindow();
	    }

	    @Override
	    public void mouseReleased(MouseEvent mouseEvent) {

		moveStart = false;

		onEndDragWindow();
	    }
	});

	basePanel = new JPanel();
	basePanel.setLayout(new BorderLayout());
	basePanel.add(buttonPanel, BorderLayout.CENTER);

	super.setContentPane(basePanel);

	contentFrame = new JPanel(new BorderLayout());
	contentFrame.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, Color.GRAY));

	JPanel resizePanel = new JPanel();
	resizePanel.setLayout(new BoxLayout(resizePanel, BoxLayout.X_AXIS));
	resizePanel.setPreferredSize(new Dimension(12, 12));
	resizePanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 12));

	final JLabel resize = new JLabel(resizeIcon);
	resize.setToolTipText("Resize");
	resize.addMouseMotionListener(new MouseMotionListener() {

	    public void mouseDragged(MouseEvent mouseEvent) {

		if(resizeStart) {
		    // noel lynch 21-08-08 : changed this for java 5
		    // compatability
		    resizeWindowTo(MouseInfo.getPointerInfo().getLocation());
		    // resizeWindowTo(mouseEvent.getLocationOnScreen());
		}

	    }

	    public void mouseMoved(MouseEvent mouseEvent) {
	    }

	});

	resize.addMouseListener(new MouseListener() {

	    public void mousePressed(MouseEvent mouseEvent) {

		// noel lynch 21-08-08 : changed this for java 5 compatability
		mouseClickPoint = MouseInfo.getPointerInfo().getLocation();
		// mouseClickPoint = mouseEvent.getLocationOnScreen();
		resizeStart = true;

		onBeginResizeWindow();
	    }

	    public void mouseReleased(MouseEvent mouseEvent) {

		resizeStart = false;

		onEndResizeWindow();
	    }

	    public void mouseClicked(MouseEvent arg0) {
	    }

	    public void mouseEntered(MouseEvent arg0) {

		resize.setIcon(resizeOverIcon);
	    }

	    public void mouseExited(MouseEvent arg0) {

		resize.setIcon(resizeIcon);
	    }
	});

	resizePanel.add(Box.createGlue());
	resizePanel.add(resize);

	contentFrame.add(resizePanel);
    }

    private void initIcons() {

	URL imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "hide.png");

	if(imageURL != null)
	    hideIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "show.png");

	if(imageURL != null)
	    showIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "close.png");

	if(imageURL != null)
	    closeIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "help.png");

	if(imageURL != null)
	    helpIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "resize.png");

	if(imageURL != null)
	    resizeIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "hide_over.png");

	if(imageURL != null)
	    hideOverIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "show_over.png");

	if(imageURL != null)
	    showOverIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "close_over.png");

	if(imageURL != null)
	    closeOverIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "help_over.png");

	if(imageURL != null)
	    helpOverIcon = new ImageIcon(imageURL);

	imageURL = A_ToolDialog.class.getClassLoader().getResource(Constants.DIALOGDATAPATH + "resize_over.png");

	if(imageURL != null)
	    resizeOverIcon = new ImageIcon(imageURL);
    }

    /**
     * Handles movement of the dialog during dragging.
     * 
     * @param point
     *            The absolute location
     */
    private void moveWindowTo(Point point) {

	int musDiffX = point.x - mouseClickPoint.x;
	int musDiffY = point.y - mouseClickPoint.y;

	setLocation(getLocation().x + musDiffX, getLocation().y + musDiffY);

	checkSide();
    }

    /**
     * For subclasses to override which custom behaviors.
     */
    protected void onBeginDragWindow() {

    }

    /**
     * For subclasses to override which custom behaviors.
     */
    protected void onEndDragWindow() {

    }

    /**
     * Handles resizing of the dialog during dragging.
     * 
     * @param point
     *            The absolute location
     */
    private void resizeWindowTo(Point point) {

	int musDiffX = point.x - mouseClickPoint.x;
	int musDiffY = point.y - mouseClickPoint.y;

	final Dimension size = getContentPane().getPreferredSize();
	size.setSize(size.width + musDiffX, size.height + musDiffY);

	getContentPane().setPreferredSize(size);
	pack();

	onHide();
	onShow();
	mouseClickPoint = point;
    }

    /**
     * For subclasses to override which custom behaviors.
     */
    protected void onBeginResizeWindow() {

    }

    /**
     * For subclasses to override which custom behaviors.
     */
    protected void onEndResizeWindow() {

    }

    /**
     * Sets the pseudo content pane of this dialog.
     * 
     * @param contentPane
     *            The content pane
     */
    @Override
    public void setContentPane(Container contentPane) {

	this.contentPane = contentPane;

	contentFrame.add(contentPane, BorderLayout.NORTH);

	if(leftSide)
	    basePanel.add(contentFrame, BorderLayout.LINE_END);
	else
	    basePanel.add(contentFrame, BorderLayout.LINE_START);
    }

    /**
     * Returns the pseudo content pane of this dialog.
     * 
     * @return The pseudo content pane
     */
    @Override
    public Container getContentPane() {

	return this.contentPane;
    }

    /**
     * Hides the dialog.
     */
    private void close() {

	this.setVisible(false);
    }

    /**
	 * 
	 */
    public abstract void onHelp();

    /**
     * @return True if the content pane is hidden, false otherwise
     */
    public boolean isHidden() {

	return hidden;
    }

    /**
     * Changes the state of the content pane from hidden to shown, or shown to
     * hidden.
     */
    public void onShowHide() {

	hidden = !hidden;

	if(hidden) {
	    stateButton.setIcon(showOverIcon);
	    onHide();
	}
	else {
	    stateButton.setIcon(hideOverIcon);
	    onShow();
	}
    }

    /**
     * Hides the content pane.
     */
    private void onHide() {

	if(!leftSide) {
	    tempTranslation = contentFrame.getBounds().width;

	    Point loc = getLocation();
	    loc.translate(tempTranslation, 0);
	    setLocation(loc);
	}
	else
	    tempTranslation = 0;

	basePanel.remove(contentFrame);

	pack();
    }

    /**
     * Shows the content pane.
     */
    private void onShow() {

	if(!leftSide) {
	    Point loc = getLocation();
	    loc.translate(-tempTranslation, 0);
	    setLocation(loc);
	}
	else {
	    Point loc = getLocation();
	    loc.translate(tempTranslation, 0);
	    setLocation(loc);
	}

	if(leftSide)
	    basePanel.add(contentFrame, BorderLayout.LINE_END);
	else
	    basePanel.add(contentFrame, BorderLayout.LINE_START);

	pack();
    }

    /**
     * Determines the side of the content pane.
     */
    private void checkSide() {

	if(leftSide) {
	    if(screenSize.width - getBounds().getMaxX() < 0) {
		leftSide = false;

		onShowHide();
		onShowHide();
	    }
	}
	else {
	    if(getLocation().x < 0) {
		leftSide = true;

		onShowHide();
		onShowHide();
	    }
	}
    }
}
